﻿using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Receiving;
using MQTTnet.Formatter;
using System;
using System.Collections.Generic;
using System.Security.Authentication;
using System.Threading.Tasks;

namespace MqttLib
{
    public class MqttClientUtil : IMqttNetBase
    {
        private bool disposedValue;

        #region 接口属性实现
        public string Username { get; private set; }
        public string Pwd { get; private set; }
        public string IP { get; private set; }
        public int Port { get; private set; }
        public uint MaxPacketSize { get; private set; }
        public MqttProtocolVersion MqttVersion { get; private set; }
        public SslProtocols SslVersion { get; private set; }
        public TimeSpan CommunicationTimeout { get; private set; }
        #endregion

        private IMqttClient _client;
        private IMqttClientOptions _options;

        public string ClientId { get; private set; }
        /// <summary>
        /// 心跳间隔
        /// </summary>
        public uint KeepAliveSendInterval { get; private set; }
        public uint KeepAlivePeriod { get; private set; }
        /// <summary>
        /// 是否保留已发布的消息
        /// </summary>
        public bool RetainAsPublished { get; private set; }

        public Action<MqttApplicationMessageReceivedEventArgs> ReceivedAction { get; set; }

        /// <summary>
        /// 订阅主题
        /// </summary>
        private List<MqttTopicFilter> _topicFilters;
        ///// <summary>
        ///// 消息构建者
        ///// </summary>
        //private MqttApplicationMessageBuilder _msgBuilder;

        /// <summary>
        /// 客户端构造函数
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="topics"></param>
        /// <param name="clientID">默认Null,自动生成</param>
        /// <param name="retainAsPublished">是否保留发布的消息,默认false</param>
        /// <param name="proVersion">默认V500</param>
        /// <param name="maxPacketSize">默认800k</param>
        /// <param name="communicationTimeout">默认36000s</param>
        /// <param name="keepAlivePeriod">默认20s</param>
        /// <param name="keepAliveSendInterval">心跳间隔,默认20s</param>
        public MqttClientUtil(string ip,
            int port,
            string username,
            string password,
            List<string> topics,
            string clientID = null,
            bool retainAsPublished = false,
            MqttProtocolVersion proVersion = MqttProtocolVersion.V500,
            uint maxPacketSize = 819200,
            uint communicationTimeout = 10,
            uint keepAlivePeriod = 36000,
            uint keepAliveSendInterval = 20
            )
        {
            IP = ip;
            Port = port;
            Username = username;
            Pwd = password;
            MqttVersion = proVersion;
            CommunicationTimeout = TimeSpan.FromSeconds(communicationTimeout);
            RetainAsPublished = retainAsPublished;
            MaxPacketSize = maxPacketSize;
            ClientId = clientID ?? Guid.NewGuid().ToString("D");
            KeepAlivePeriod = keepAlivePeriod;
            KeepAliveSendInterval = keepAliveSendInterval;

            if (topics?.Count > 0)
            {
                _topicFilters = new List<MqttTopicFilter>(topics.Count);
                foreach (string topic in topics)
                    _topicFilters.Add(new MqttTopicFilter { Topic = topic, RetainAsPublished = RetainAsPublished });
            }
        }

        /// <summary>
        /// 初始化
        /// </summary>
        private void Init()
        {
            _client?.Dispose();

            MqttClientOptionsBuilder optionsBuilder = new MqttClientOptionsBuilder();
            _options = optionsBuilder.WithTcpServer(IP, Port)
                .WithCredentials(Username, Pwd)
                .WithClientId(ClientId)
                .WithCommunicationTimeout(CommunicationTimeout)
                .WithKeepAlivePeriod(TimeSpan.FromSeconds(KeepAlivePeriod))
                .WithMaximumPacketSize(MaxPacketSize)
                .Build();

            _client = IMqttNetBase.Factory.CreateMqttClient();
            _client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(ReceivedAction);
            _client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(ClientConnected);
            _client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(ClientDisconnected);
            //_msgBuilder = new MqttApplicationMessageBuilder()
            //    .WithContentType("string")
            //    .WithAtLeastOnceQoS()
            //    .WithRetainFlag(RetainAsPublished);
        }

        /// <summary>
        /// 启动客户端
        /// </summary>
        /// <returns></returns>
        public async Task<bool> StartAsync()
        {
            if (_client == null) { Init(); }
            if (_client.IsConnected) { return true; }

            MqttClientConnectResult mqttRs = await _client.ConnectAsync(_options);
            bool connRs = mqttRs.ResultCode == MqttClientConnectResultCode.Success;
            if (connRs && _topicFilters?.Count > 0)
            {
                await _client.SubscribeAsync(_topicFilters.ToArray());
            }
            return connRs;
        }

        /// <summary>
        /// 停止客户端
        /// </summary>
        /// <returns></returns>
        public async Task StopAsync()
        {
            await _client.DisconnectAsync();
        }

        public async Task<MqttClientPublishResult> Publish(MqttApplicationMessage msg)
        {
            if (_client.IsConnected == false) // 掉线重连
            {
                MqttClientConnectResult mqttRs = await _client.ReconnectAsync();
            }
            if (_client.IsConnected)
            {
                //MqttApplicationMessage msg = _msgBuilder.Build();
                //msg.Topic = topic;
                //msg.Payload = data;
                return await _client.PublishAsync(msg);
            }
            return new MqttClientPublishResult
            {
                ReasonCode = MqttClientPublishReasonCode.UnspecifiedError,
                ReasonString = "客户端断开连接，重连失败!"
            };
        }

        /// <summary>
        /// 客户端上线事件
        /// </summary>
        /// <param name="args"></param>
        private void ClientConnected(MqttClientConnectedEventArgs args)
        {
            Console.WriteLine($"ClientId:{ClientId} has connected success!");
        }

        /// <summary>
        /// 客户端离线事件
        /// </summary>
        /// <param name="args"></param>
        private void ClientDisconnected(MqttClientDisconnectedEventArgs args)
        {
            Console.WriteLine($"ClientId:{ClientId} has disconnected!{args.Reason}");
        }


        protected void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)
                    try { StopAsync().Wait(); } catch { }
                    _client.Dispose();
                }
                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // TODO: 将大型字段设置为 null
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        //// TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器
        //~MqttNetBase()
        //{
        //    // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
        //    Dispose(disposing: false);
        //}

    }
}